正規表達式
正規表達式
Regular Expression
從入門到入土
放在前面
Regex
- 適用於: 所有需要字串驗證、比對、搜尋、擷取等場景
- 不適用於: 長文匹配(效能問題) 不支援環境
工具
引入
各位是否遇過需要搜尋特定字串的情況?
假定要從Youtube網址中擷取出影片ID(v=xxxxxxxx)
在輸出3HDP4xp8Oj4
的前提下,各位會如何做呢?
https://www.youtube.com/watch?v=3HDP4xp8Oj4&feature=youtu.be
使用String.split? 使用URLSearchParams?
或者... 使用正規表達式!?
規則
- \
- ?、+、*、{}
- ()、|
- []
- ^、$、\b
- (?=)
- flag
字元匹配
在Regex內的一般文字將直接配對
/abc/ // 匹配abc
"123abc456".match(/abc/)
// ['abc', index: 3, input: '123abc456', groups: undefined]
「.」 可以匹配\r\n
(換行字元)以外任何字元
/tr.../ // tr以及任意三個字元
"trick or treat".match(/tr.../g)
// ['trick', 'treat']
集合
[]
中括號內的字元,
會以「或」的形式當成一個普通字元配對
/[早午晚]安/ // 早安 or 午安 or 晚安
"早安 午安 晚安 平安".match(/[早午晚]安/g)
// ['早安', '午安', '晚安']
[]
內可以使用「-」
表示連續的「或」
/[0-9A-Za-z]/ // 數字+英文字母大小寫
"59分 87分 8a分".match(/[6-9][0-9]分/g)
// ['87分']
反集合
在[]
的開頭加入跳脫字元「^」
代表除了涵蓋內的字元都可以配對
同樣可以使用「-」
表示連續排除
/[^abc123ABC]/
// 排除小寫a-z與空格
"Apple book Cat Docker".match(/[^a-z ]../g)
// ['App', 'Cat', 'Doc']
量詞
{}
量詞均代表前面的表達式重複次數
{x}
剛好出現x次{x,}
至少出現x次,至多無限次{x,y}
至少出現x次,且至多只能出現y次, 同時x<=y (否則報錯)- ⚠特別注意,
xy
只能是數字, 並且,
間不能有空格
想想看如果放入非數字,或者加空格會發生什麼? 點我看答案
量詞-縮寫
?
代表前面的表達式可有可無 (0~1次) ={0,1}
+
代表前面的表達式至少一次 (1~inf次) ={1,}
*
代表前面的表達式隨意出現 (0~inf次) ={0,}
/https?:\/{2}example.com/ /*匹配example.com,且https或http皆可*/
/[0-9]+.[0-9]+/ /*匹配浮點數(有潛在問題)*/
/.*/ /*隨意匹配*/
想一下第二個範例的浮點數潛在什麼問題? 點我看答案
非貪心量化
詳細介紹 Regular expression: 貪婪、非貪婪
簡單來講,在量詞後面加上?
會盡可能減少配對
re = /阿.+是蟑螂!*/
re2 = /阿.+?是蟑螂!*/
str = "阿啊啊啊是蟑螂啊啊啊幹幹幹幹幹幹是蟑螂!!!!ldsjghiwjp"
/* re <------------------------------------>
re2 <---------->
貪婪與非貪婪的配對數量差距
*/
特殊字元「\」
反斜線\
可以將字元轉譯,
包含轉譯成特殊字元(bBdDfnrsStuvwWx
),
以及轉譯回普通字元(^$()*+?.[\{|
)
\f
匹配分頁符號(form-feed)\n
匹配換行(line-feed)\r
匹配回車(return)\t
匹配製表符(horizontal tab)\v
匹配垂直製表符(vertical tab)\x--
以16進位的ASCII code匹配 (00~ff)\u----
以10進位unicode匹配 (0000~9999)
特殊字元-縮寫
符號 | 等效縮寫 | 匹配內容 |
---|---|---|
\d | [0-9] | 數字(*註1) |
\D | [^0-9] | 非數字 |
\s | [\r\n\t\f\v ] | 空格、換行等(*註2) |
\S | [^\r\n\t\f\v ] | 非空格、換行等 |
\w | [A-Za-z0-9_] | 單字字元(*註3) |
\W | [^A-Za-z0-9_] | 非單字字元 |
以下狀況於Unicode的Regex出現
- 註1:
\d\D
會符合/排除全形數字字元 - 註2:
\s\S
會符合/排除全形、特殊空格 - 註3:
\w\W
會符合/排除各國文字
群組
()
小括號有三個功能
- 提高匹配的優先順序
- 把一段表達式包裹起來(通常用於量化)
- 將括號內匹配到的擷取為群組輸出
"abc".match(/([a-z]+)/)[1] // abc
"abc".match(/[a-z]+/)[1] // undefined
或
|
將表達式分隔成左右兩段,以「或」的形式匹配
如果未放置在括號內,則作用在全域
"fool".match(/food|l/) // l
"fool".match(/foo(d|l)/) // fool
命名/非捕捉 群組
(?<name>)
在括號開頭加上?<>
,
同時在<>
內輸入文字即可為群組命名
(?:)
如果不需要括號的捕捉功能,
可在括號開頭加上?:
"25歲".match(/(?<age>\d+)/).groups?.age // 25
"25歲".match(/(\d+)/).groups?.age // undefined
"abc".match(/([a-z]+)/)[1] // abc
"abc".match(/(?:[a-z]+)/)[1] // undefined
向後參考
()\num
當群組捕捉到文字時,
可在其後方使用向後參考匹配相同的文字
\num
意思為第num個被捕捉到的文字
"一心一意".match(/(.).\1./) // 一心一意
"一石二鳥".match(/(.).\1./) // null
定位匹配
^
匹配至字串開頭$
匹配至字串結尾\b
匹配至單字邊界\B
匹配非單字邊界
"123".match(/^\d+$/) // 123
"a123".match(/^\d+$/) // null
(/er\b/).test("never") // true
(/er\b/).test("version") // false
向前/後看
「在一個字串中找出連續數字 6~8 個」 "12345 XD Hi12345678ab666666cd987654321" 只能輸出:12345678 和 666666 思考一下如何解決? 點我看解答
X
與Y
分別為一段表達式,
在表達式X
的前/後加入(?=)
或(?!)
,
可以指定表達式X
前/後方須出現的表達式Y
。
※如果Y
放在X
前方,
則?
的後方需要多一個小於(<
)符號
ahead與behind可以合併使用, 且不計入匹配範圍
- look ahead
- Positive: X(?=Y)
- Negative: X(?!Y)
- look behind
- Positive: (?<=Y)X
- Negative: (?<!Y)X
RegExp 應用: lookahead , lookbehind
優先級
優先權 | 符號 |
---|---|
最高 | \ |
高 | () 、(?:) 、(?=) 、[] |
中 | 量詞 * 、+ 、? 、{n} |
低 | ^ 、$ 、中介字元 |
次低 | 相鄰字元 |
最低 | \| |
Flag
flag通常放置於雙斜線後方
/[\w\W]*/gmiu
符號 | 全稱 | 含義 |
---|---|---|
g | Global | 匹配所有可能 |
m | Multi line | ^ 、$ 可以匹配\r\n |
i | Insensitive | 大小寫不敏感 |
u | Unicode | 以unicode進行匹配 |
練習
結語
雖然Regex可以做的事很多, 但通常善用第三方lib會是更好的選擇。 例如時間驗證等等
參考
- 規則一覽Wiki
- RegExp 應用: lookahead , lookbehind
- Regular expression: 貪婪、非貪婪
- 淺談 regex 及其應用
- [JS] 正則表達式(Regular Expression, regex)